package org.msb.code.thread;

import java.util.LinkedList;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @Package org.msb.code.thread
 * @PROJECT_NAME: question
 * @DESCRIPTION:
 * @USER: zhuchenglin
 * @DATE: 2022/6/3 19:21
 */
public class TestProducerConsumer {
    /**
     * 写一个固定容量同步容器，拥有put和get方法，以及getCount方法，能够支持2个生产者线程以及10个消费者线程的阻塞调用
     */
    static class TestSync<T> {

        //        static List<Object> list = new ArrayList<>(10);
        final LinkedList<T> list = new LinkedList<>();
        static final int MAX = 10;
        public int count = 0;

        synchronized void put(T t) {
            while (list.size() == MAX) {
                try {
                    this.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            list.add(t);
            ++count;
            this.notifyAll();
        }

        synchronized T get() {
            T t;
            while (list.size() == 0) {
                try {
                    this.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            t = list.removeFirst();
            count--;
            this.notifyAll();
            return t;
        }

        public static void main(String[] args) {
            TestSync c = new TestSync();

            // 2个线程生产数据
            for (int i = 0; i < 2; i++) {
                new Thread(() -> {
                    for (int j = 0; j < 25; j++) {
                        c.put(Thread.currentThread().getName() + " " + j);
                    }
                }, "p" + i).start();
            }

            // 10个线程消费数据
            for (int i = 0; i < 10; i++) {
                new Thread(() -> {
                    for (int j = 0; j < 5; j++) {
                        System.out.println(Thread.currentThread().getName() + ":consumer (" + c.get() + ")");
                    }
                }, "c" + i).start();
            }
        }
    }

    /**
     * 写一个固定容量同步容器，拥有put和get方法，以及getCount方法，能够支持2个生产者线程以及10个消费者线程的阻塞调用
     */
    static class TestCondition<T> {

        //        static List<Object> list = new ArrayList<>(10);
        final LinkedList<T> list = new LinkedList<>();
        static final int MAX = 10;
        public int count = 0;

        private Lock lock = new ReentrantLock();
        private Condition producer = lock.newCondition(); // 不同的等待队列
        private Condition consumer = lock.newCondition();

        void put(T t) {
            try {
                lock.lock();
                while (list.size() == MAX) {
                    try {
                        producer.await();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                list.add(t);
                ++count;
                consumer.signalAll();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }

        synchronized T get() {
            T t = null;
            try {
                lock.lock();
                while (list.size() == 0) {
                    try {
                        consumer.await();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                t = list.removeFirst();
                count--;
                producer.signalAll();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
            return t;
        }

        public static void main(String[] args) {
            TestCondition c = new TestCondition();

            // 2个线程生产数据
            for (int i = 0; i < 2; i++) {
                new Thread(() -> {
                    for (int j = 0; j < 25; j++) {
                        c.put(Thread.currentThread().getName() + " " + j);
                    }
                }, "p" + i).start();
            }

            // 10个线程消费数据
            for (int i = 0; i < 10; i++) {
                new Thread(() -> {
                    for (int j = 0; j < 5; j++) {
                        System.out.println(Thread.currentThread().getName() + ":consumer (" + c.get() + ")");
                    }
                }, "c" + i).start();
            }
        }
    }

}
